Python问题解析-关于__getattr__的链式调用的应用的一点解析
2017-11-23
<span style=”background-color: red>关于
__getattr__()的链式调用的应用的一点解析实例: 现在很多网站都搞REST API,比如新浪微博、豆瓣啥的,调用API的URL类似:
http://api.server/user/friends
http://api.server/user/timeline/list
如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。利用完全动态的getattr,我们可以写出一个链式调用:
1
2
3
4
5
6
7
8
9
10
11
12class Chain(object):
def __init__(self, path=''):
self._path = path
def __getattr__(self, path):
return Chain('%s/%s' % (self._path, path))
def __str__(self):
return self._path
__repr__ = __str__尝试:
1
2>>> Chain().status.user.timeline.list
'/status/user/timeline/list'疑问: Chain().status.user.timeline.list在类Chain()内部是如何工作的才导致输出为:
‘/status/user/timeline/list’?解析:
只分析一层属性和两层属性, 即:
Chain().status与Chain().status.user. 在类Chain()中添加print, 新代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class Chain(object):
def __init__(self, path=''):
self._path = path
print("init path is:" + self._path)
def __getattr__(self, path):
print("get path is:" + path)
return Chain('%s/%s' % (self._path, path))
def __str__(self):
return self._path
__repr__ = __str__
# print(c)
print('---------------------------')
print(Chain().status)
print('---------------------------')
print(Chain().status.user)
print('---------------------------')结果是:
1
2
3
4
5
6
7
8
9
10
11
12
13---------------------------
init path is:
get path is:status
init path is:/status
/status
---------------------------
init path is:
get path is:status
init path is:/status
get path is:user
init path is:/status/user
/status/user
---------------------------分析结果:
print(Chain().status)经历了两次__init__, 再结合源代码中的return Chain('%s/%s' % (self._path, path))分析, 可知:Chain的新实例的status属性触发__getattr__方法, 而该方法对Chain()新实例进行了递归操作, 带入新实例的值为/status. 此时, 新实例二没有属性, 因此不会调用__getattr__方法. 而print(Chain('/status'))会调用__str__方法显示参数path即/statusprint(Chain().status.user):1中的新实例二会在2中拥有属性user, 再次调用__getattr__,*.user之后的属性会再再次调用__getattr__, 从而打印出相应路径